package org.streets.eis.component.modaless;

import java.util.ArrayList;
import java.util.List;

import org.apache.wicket.Component;
import org.apache.wicket.IPageMap;
import org.apache.wicket.Page;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.ResourceReference;
import org.apache.wicket.Session;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxCallDecorator;
import org.apache.wicket.ajax.calldecorator.CancelEventIfNoAjaxDecorator;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.CloseButtonCallback;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.PageCreator;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow.WindowClosedCallback;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.JavascriptPackageResource;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.html.resources.CompressedResourceReference;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.RequestParameters;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.Strings;
import org.streets.eis.component.WebResources;

/**
 * based on ModalWindow.java
 * 可设置cookieName来缓存窗口位置和大小，使用showPage方法时默认采用的是要打开的page名
 *  默认为打开子窗口，showPage时可以修改默认值
 */
public class ModalessDialog extends Panel{
	
	private static final long serialVersionUID = 1L;

	public final static String CSS_CLASS_BLUE = "w_blue";

	public final static String CSS_CLASS_GRAY = "w_silver";

	private boolean deletePageMap = false;

	protected boolean shown = false;

	private WebMarkupContainer empty;

	private int minimalWidth = 200;
	private int minimalHeight = 200;
	private String cssClassName = CSS_CLASS_GRAY;
	private int initialWidth = 600;
	private int initialHeight = 400;
	private boolean useInitialHeight = true;
	private boolean resizable = true;
	private String widthUnit = "px";
	private String heightUnit = "px";
	private String cookieName;
	private IModel<String> title = null;

	private String pageMapName = "modaless-dialog-pagemap";

	private PageCreator pageCreator = null;
	private CloseButtonCallback closeButtonCallback = null;
	private WindowClosedCallback windowClosedCallback = null;

	private boolean showAsChild = true;
	
	protected List<String> children = new ArrayList<String>();

	private int idx = 0;
	
	protected static final ResourceReference CSS = new CompressedResourceReference(ModalWindow.class,"res/modal.css");

	public ModalessDialog(final String id){
		super(id);
		init();
	}

	public ModalessDialog(final String id, final IModel<?> model){
		super(id, model);
		init();
	}

	protected void init(){
		setVersioned(false);
		cookieName = null;

		add(empty = new WebMarkupContainer(getContentId()));

		add(new CloseButtonBehavior());
		add(new WindowClosedBehavior());

		add(JavascriptPackageResource.getHeaderContribution(WebResources.Streets.Modaless.JS));
		add(CSSPackageResource.getHeaderContribution(CSS));
	}

	public boolean isShown(){
		return shown;
	}

	public ModalessDialog setPageMapName(final String pageMapName){
		this.pageMapName = pageMapName;
		return this;
	}

	public String getPageMapName(){
		return pageMapName;
	}

	public ModalessDialog setPageCreator(final PageCreator creator){
		setContent(empty);
		pageCreator = creator;
		return this;
	}

	public ModalessDialog setCloseButtonCallback(final CloseButtonCallback callback){
		closeButtonCallback = callback;
		return this;
	}

	public ModalessDialog setWindowClosedCallback(final WindowClosedCallback callback){
		windowClosedCallback = callback;
		return this;
	}

	public void show(final AjaxRequestTarget target){
		if (shown == false){
			getContent().setVisible(true);
			target.addComponent(this);
			target.appendJavascript(getWindowOpenJavascript());
			shown = true;
		}
	}

	public static final void closeCurrent(AjaxRequestTarget target){
		target.appendJavascript(getCloseJavacript());
	}

	public void close(final AjaxRequestTarget target){
		getContent().setVisible(false);
		target.appendJavascript(getCloseJavacript());
		shown = false;
	}
	
	public void closeChildren(final AjaxRequestTarget target){
		target.appendJavascript(getCloseChildrenJavacript(children));
		children.clear();
	}	

	protected static String getCloseJavacript(){
		return "var win;\n" //
			+ "try {\n"
			+ "	win = window.parent.Streets.Window;\n"
			+ "} catch (ignore) {\n"
			+ "}\n"
			+ "if (!win || !win.current) {\n"
			+ "  try {\n"
			+ "     win = window.Streets.Window;\n"
			+ "  } catch (ignore) {\n"
			+ "  }\n"
			+ "}\n"
			+ "if (win && win.current) {\n"
			+ " var close = function(w) { w.setTimeout(function() {\n"
			+ "		win.current.close();\n"
			+ "	}, 0);  } \n"
			+ "	try { close(window.parent); } catch (ignore) { close(window); };\n" + "}";	
	}
	
	protected static String getCloseChildrenJavacript(List<String> winIds){
		StringBuffer js = new StringBuffer("var win;");
		
		for (String winId : winIds){
			js.append("try{\n")
			  .append("  win = document.getElementById('").append(winId).append("');\n")
			  .append("  if (win){win=win['win']; if(win){win.close();}}\n")
			  .append("  win = window.parent.document.getElementById('").append(winId).append("');\n")
			  .append("  if (win){win=win['win']; if(win){win.close();}}\n")
			  .append("} catch (ignore) {}\n");
		}
		
		return js.toString();
	}

	public String getContentId(){
		return "content";
	}

	public ModalessDialog setMinimalWidth(int minimalWidth){
		this.minimalWidth = minimalWidth;
		return this;
	}

	public int getMinimalWidth(){
		return minimalWidth;
	}

	public ModalessDialog setMinimalHeight(int minimalHeight){
		this.minimalHeight = minimalHeight;
		return this;
	}

	public int getMinimalHeight(){
		return minimalHeight;
	}

	public ModalessDialog setCssClassName(String cssClassName){
		this.cssClassName = cssClassName;
		return this;
	}

	public String getCssClassName(){
		return cssClassName;
	}

	public ModalessDialog setInitialWidth(int initialWidth){
		this.initialWidth = initialWidth;
		return this;
	}

	public int getInitialWidth(){
		return initialWidth;
	}

	public ModalessDialog setInitialHeight(int initialHeight){
		this.initialHeight = initialHeight;
		return this;
	}

	public int getInitialHeight(){
		return initialHeight;
	}

	public ModalessDialog setUseInitialHeight(boolean useInitialHeight){
		this.useInitialHeight = useInitialHeight;
		return this;
	}

	public boolean isUseInitialHeight(){
		return useInitialHeight;
	}

	public ModalessDialog setResizable(boolean resizable){
		this.resizable = resizable;
		return this;
	}

	public boolean isResizable(){
		return resizable;
	}

	public ModalessDialog setWidthUnit(String widthUnit){
		this.widthUnit = widthUnit;
		return this;
	}

	public String getWidthUnit(){
		return widthUnit;
	}

	public ModalessDialog setHeightUnit(String heightUnit){
		this.heightUnit = heightUnit;
		return this;
	}

	public String getHeightUnit(){
		return heightUnit;
	}

	public ModalessDialog setCookieName(String cookieName){
		if (cookieName != null && cookieName.indexOf(",") != -1 && cookieName.indexOf("|") != -1){
			throw new IllegalArgumentException("Cookie name may not contain ',' or '|' characters.");
		}
		this.cookieName = cookieName;
		return this;
	}

	public String getCookieName(){
		return cookieName;
	}

	public ModalessDialog setTitle(String title){
		this.title = new Model<String>(title);
		return this;
	}

	public ModalessDialog setTitle(IModel<String> title){
		title = wrap(title);
		this.title = title;
		return this;
	}

	public IModel<String> getTitle(){
		return title;
	}
	
	public boolean isShowAsChild() {
		return showAsChild;
	}

	public ModalessDialog setShowAsChild(boolean showAsChild) {
		this.showAsChild = showAsChild;
		return this;
	}	

	private Page createPage(){
		if (pageCreator == null){
			return null;
		} else {
			RequestParameters parameters = RequestCycle.get().getRequest().getRequestParameters();
			String oldPageMapName = parameters.getPageMapName();

			if (getPageMapName() != null){
				Session session = Session.get();
				if (session.pageMapForName(getPageMapName(), false) == null){
					deletePageMap = true;
				}
				parameters.setPageMapName(getPageMapName());
			}

			try{
				return pageCreator.createPage();
			} finally {
				parameters.setPageMapName(oldPageMapName);
			}
		}
	}

	@Override
	protected void onBeforeRender(){
		shown = makeContentVisible();

		getContent().setOutputMarkupId(true);
		getContent().setVisible(shown);

		super.onBeforeRender();
	}

	protected boolean makeContentVisible(){
		if (getWebRequest().isAjax() == false){
			return false;
		} else {
			return shown;
		}
	}

	@Override
	protected void onComponentTag(final ComponentTag tag){
		super.onComponentTag(tag);
		tag.put("style", "display:none");
	}

	protected final Component getContent(){
		return get(getContentId());
	}

	private boolean isCustomComponent(){
		return getContent() != empty;
	}

	@Override
	public void remove(Component component){
		super.remove(component);
		if (component.getId().equals(getContentId())){
			add(empty = new WebMarkupContainer(getContentId()));
		}
	}

	public ModalessDialog setContent(final Component component){
		if (component.getId().equals(getContentId()) == false){
			throw new WicketRuntimeException("Modal window content id is wrong. Component ID:" +
				component.getId() + "; content ID: " + getContentId());
		}

		component.setOutputMarkupPlaceholderTag(true);
		component.setVisible(false);
		replace(component);
		shown = false;
		pageCreator = null;
		return this;
	}

	protected class WindowClosedBehavior extends AbstractDefaultAjaxBehavior{
		private static final long serialVersionUID = 1L;

		@Override
		protected void respond(AjaxRequestTarget target){
			shown = false;

			if (deletePageMap == true){
				Session session = Session.get();
				IPageMap pageMap = session.pageMapForName(getPageMapName(), false);

				if (pageMap != null){
					session.removePageMap(pageMap);
					deletePageMap = false;
				}
			}

			if (windowClosedCallback != null){
				windowClosedCallback.onClose(target);
			}
		}

		@Override
		protected CharSequence getCallbackScript(){
			return super.getCallbackScript();
		}
	};


	protected class CloseButtonBehavior extends AbstractDefaultAjaxBehavior{
		private static final long serialVersionUID = 1L;

		@Override
		protected void respond(AjaxRequestTarget target){
			if (closeButtonCallback == null || closeButtonCallback.onCloseButtonClicked(target) == true){
				close(target);
			}
		}

		@Override
		protected IAjaxCallDecorator getAjaxCallDecorator(){
			return new CancelEventIfNoAjaxDecorator(super.getAjaxCallDecorator());
		}

		@Override
		protected CharSequence getCallbackScript(){
			return super.getCallbackScript();
		}
	}

	protected String getContentMarkupId(){
		return getContent().getMarkupId();
	}

	protected String getWindowMarkupId(){
		return String.format("%s-%d",getMarkupId(),++idx);
	}
	
	protected String escapeQuotes(String string){
		if (string.indexOf('"') != -1){
			string = Strings.replaceAll(string, "\"", "\\\"").toString();
		}
		return string;
	}

	protected final String getWindowOpenJavascript(){
		AppendingStringBuffer buffer = new AppendingStringBuffer(500);

		if (isCustomComponent() == true){
			buffer.append("var element = document.getElementById(\"");
			buffer.append(getContentMarkupId());
			buffer.append("\");\n");
		}

		buffer.append("var settings = new Object();\n");
		
		String winId = getWindowMarkupId();
		appendAssignment(buffer, "settings.id", winId);
		children.add(winId);
		
		appendAssignment(buffer, "settings.minWidth", getMinimalWidth());
		appendAssignment(buffer, "settings.minHeight", getMinimalHeight());
		appendAssignment(buffer, "settings.className", getCssClassName());
		appendAssignment(buffer, "settings.width", getInitialWidth());

		if (isUseInitialHeight() == true || isCustomComponent() == false){
			appendAssignment(buffer, "settings.height", getInitialHeight());
		} else {
			buffer.append("settings.height=null;\n");
		}

		appendAssignment(buffer, "settings.resizable", isResizable());

		if (isResizable() == false){
			appendAssignment(buffer, "settings.widthUnit", getWidthUnit());
			appendAssignment(buffer, "settings.heightUnit", getHeightUnit());
		}

		if (isCustomComponent() == false){
			Page page = createPage();
			if (page == null){
				throw new WicketRuntimeException("Error creating page for modal dialog.");
			}
			RequestCycle.get().setUrlForNewWindowEncoding();

			appendAssignment(buffer, "settings.src", RequestCycle.get().urlFor(page));

			if (getPageMapName() != null){
				appendAssignment(buffer, "settings.iframeName", getPageMapName());
			}
		} else {
			buffer.append("settings.element=element;\n");
		}

		if (getCookieName() != null){
			appendAssignment(buffer, "settings.cookieId", getCookieName());
		}

		Object title = getTitle() != null ? getTitle().getObject() : null;
		if (title != null){
			appendAssignment(buffer, "settings.title", escapeQuotes(title.toString()));
		}
		
		if (showAsChild){
			buffer.append("settings.showAsChild=1;\n");
		}

		boolean haveCloseCallback = false;

		if ((isCustomComponent() == false && deletePageMap) || windowClosedCallback != null){
			WindowClosedBehavior behavior = getBehaviors(WindowClosedBehavior.class).get(0);
			buffer.append("settings.onClose = function() { ");
			buffer.append(behavior.getCallbackScript());
			buffer.append(" };\n");

			haveCloseCallback = true;
		}

		if (closeButtonCallback != null || haveCloseCallback == false){
			CloseButtonBehavior behavior = getBehaviors(CloseButtonBehavior.class).get(0);
			buffer.append("settings.onCloseButton = function() { ");
			buffer.append(behavior.getCallbackScript());
			buffer.append("};\n");
		}

		postProcessSettings(buffer);

		buffer.append("Streets.Window.create(settings).show();\n");
		return buffer.toString();
	}

	private void appendAssignment(final AppendingStringBuffer buffer, final CharSequence key,final int value){
		buffer.append(key).append("=");
		buffer.append(value);
		buffer.append(";\n");
	}

	private void appendAssignment(final AppendingStringBuffer buffer, final CharSequence key,final boolean value){
		buffer.append(key).append("=");
		buffer.append(Boolean.toString(value));
		buffer.append(";\n");
	}

	private void appendAssignment(final AppendingStringBuffer buffer, final CharSequence key,final CharSequence value){
		buffer.append(key).append("=\"");
		buffer.append(value);
		buffer.append("\";\n");
	}

	protected AppendingStringBuffer postProcessSettings(AppendingStringBuffer settings){
		return settings;
	}

	@Override
	protected void onDetach(){
		super.onDetach();

		if (title != null){
			title.detach();
		}
	}
	

	public void showPage(boolean showAsChild,String title,final Page page,AjaxRequestTarget target,
			ModalWindow.WindowClosedCallback closedCallback,
			ModalWindow.CloseButtonCallback closeButtonCallback,
			int minWidth,int minHeight,int initWidth,int initHeight){
		
		setPageCreator(new PageCreator(){
			private static final long serialVersionUID = 1L;
			public Page createPage() {
				return page;
			}			
		});
		if (Strings.isEmpty(cookieName)){
			setCookieName(page.getClass().getName().replaceAll("\\.", "-"));
		}
		
		setShowAsChild(showAsChild);
		
		if (!Strings.isEmpty(title)) { 
			setTitle(title);
		}
		
		if (closedCallback!=null){
			setWindowClosedCallback(closedCallback);
		}
		
		if (closeButtonCallback!=null){
			setCloseButtonCallback(closeButtonCallback);
		}
		
		if (minWidth>0){
			setMinimalWidth(minWidth);
		}
		
		if (minHeight>0){
			setMinimalHeight(minHeight);
		}
		
		if (initWidth>0){
			setInitialWidth(initWidth);
		}
		
		if (initHeight>0){
			setInitialHeight(initHeight);
		}
		
		show(target);
	}
	
	public void showPage(boolean showAsChild,String title,final Page page,AjaxRequestTarget target,
			ModalWindow.WindowClosedCallback closedCallback,
			ModalWindow.CloseButtonCallback closeButtonCallback){
		showPage(showAsChild,title,page,target,closedCallback,closeButtonCallback,0,0,0,0);
	}
	
	public void showPage(boolean showAsChild,String title,final Page page,AjaxRequestTarget target,
			ModalWindow.WindowClosedCallback closedCallback){
		showPage(showAsChild,title,page,target,closedCallback,null,0,0,0,0);
	}
	
	public void showPage(boolean showAsChild,String title,final Page page,AjaxRequestTarget target){
		showPage(showAsChild,title,page,target,null,null,0,0,0,0);
	}
	
	public void showPage(boolean showAsChild,final Page page,AjaxRequestTarget target){
		showPage(showAsChild,null,page,target,null,null,0,0,0,0);
	}	
	
	public void showPage(String title,final Page page,AjaxRequestTarget target,
			ModalWindow.WindowClosedCallback closedCallback,
			ModalWindow.CloseButtonCallback closeButtonCallback){
		showPage(showAsChild,title,page,target,closedCallback,closeButtonCallback,0,0,0,0);
	}
	
	public void showPage(String title,final Page page,AjaxRequestTarget target,
			ModalWindow.WindowClosedCallback closedCallback){
		showPage(showAsChild,title,page,target,closedCallback,null,0,0,0,0);
	}
	
	public void showPage(String title,final Page page,AjaxRequestTarget target){
		showPage(showAsChild,title,page,target,null,null,0,0,0,0);
	}
	
	public void showPage(final Page page,AjaxRequestTarget target){
		showPage(showAsChild,null,page,target,null,null,0,0,0,0);
	}		
}
